package com.kangfu.api.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kangfu.api.service.IHuaweiService;
import com.kangfu.common.constant.CacheConstant;
import com.kangfu.common.constant.HuaweiConstant;
import com.kangfu.common.constant.SystemConstant;
import com.kangfu.common.exception.BusinessException;
import com.kangfu.common.utils.*;
import com.kangfu.db.domain.entity.HuaweiHealthBluetoohDetail;
import com.kangfu.db.domain.entity.User;
import com.kangfu.db.domain.entity.UserOperateLog;
import com.kangfu.db.mapper.HuaweiHealthBluetoohDetailMapper;
import com.kangfu.db.mapper.UserMapper;
import com.kangfu.db.mapper.UserOperateLogMapper;
import com.kangfu.db.model.bo.huawei.*;
import com.kangfu.db.model.request.ReceviceDataRequest;
import com.kangfu.db.model.request.huawei.QuerySampleSetRequest;
import com.kangfu.wechat.utils.JsonUtil;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.math.BigDecimal;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class HuaweiServiceImpl implements IHuaweiService {

    private static final Logger logger = LoggerFactory.getLogger(HuaweiServiceImpl.class);

    private final String redirectUri = SystemConstant.API_URL + "/huawei/getAccessToken";


    @Autowired
    public RedisUtils redisUtils;

    @Autowired
    public UserMapper userMapper;

    @Autowired
    public UserOperateLogMapper userOperateLogMapper;
    
    @Autowired
    public HuaweiHealthBluetoohDetailMapper huaweiHealthBluetoohDetailMapper;

    /**
     * 构建 h5 授权链接
     * @return
     */
    @Override
    public String buildAuthorizeUrl(int userId, String token) {
        HashMap<String, String> requestParam = new HashMap<String, String>();
        requestParam.put("client_id", HuaweiConstant.CLIENT_ID);
        requestParam.put("response_type","code");
        requestParam.put("redirect_uri",this.redirectUri);
        String radomString = RandomUtil.randomString(50);
        requestParam.put("state", radomString);
        requestParam.put("display", "touch");
        redisUtils.set(radomString,userId,3600); //state入库，后面跳转回来到redirectUri的时候会带state参数，如果返回的state不存在redis里面，说明时间太久了，就不处理了
        redisUtils.set(radomString + "_token",token,3600); //state入库，后面跳转回来到redirectUri的时候会带state参数，如果返回的state不存在redis里面，说明时间太久了，就不处理了
        requestParam.put("scope","openid " + HuaweiConstant.STEP_READ + " " + HuaweiConstant.DISTANCE_READ + " " + HuaweiConstant.CALORIES_READ);
        requestParam.put("access_type","offline");
        String huaweiAuthUrl = UrlUtils.parseParams(HuaweiConstant.AUTHORIZE_URL,requestParam);
        huaweiAuthUrl = huaweiAuthUrl.replaceAll("%20","+");
        return huaweiAuthUrl;
    }

    /**
     * 获取access_token
     * @param code
     */
    @Override
    public void getAccessTokenByCode(String code, int userId) {
        //根据code获取accessToken
        OkHttpClient okHttpClient = new OkHttpClient();

        RequestBody body = new FormBody.Builder()
                .add("grant_type","authorization_code")
                .add("code",code)
                .add("client_id",HuaweiConstant.CLIENT_ID)
                .add("client_secret", HuaweiConstant.CLIENT_SECRET)
                .add("redirect_uri",this.redirectUri)
                .build();
        Request request = new Request.Builder()
                .url(HuaweiConstant.TOKEN_URL)
                .post(body)
                .build();

        try {
            Response response = okHttpClient.newCall(request).execute();
            if (response.isSuccessful()) { // 判断是否成功
                String string = response.body().string();
                logger.info("用户绑定手环 用户id: " + String.valueOf(userId) + " 获取accesstoken: 成功" + string);
                HuaweiAccessTokenBo huaweiAccessTokenBo = JsonUtil.parseObject(string, HuaweiAccessTokenBo.class);
                if (huaweiAccessTokenBo == null) {
                    throw new BusinessException("获取华为 accessToken 失败");
                }
                AccessTokenBo accessTokenBo = new AccessTokenBo();
                accessTokenBo.buildByHuaweiAccessTokenBo(huaweiAccessTokenBo);
                //access_token存入redis
                redisUtils.set(CacheConstant.HUAWEI_ACCESS_TOKEN_PREFIX + userId,accessTokenBo);
                User user = new User();
                user.setId(userId);

                User findUser = userMapper.selectUserById(userId);
                if (findUser != null && findUser.getFirstBindTime() != null) {
                    logger.info("用户绑定手环 非第一次 用户id: " + String.valueOf(userId));
                } else {
                    logger.info("用户绑定手环 第一次 用户id: " + String.valueOf(userId));
                    user.setFirstBindTime(LocalDateTime.now());
                }
                //检查授权是否完整
                String scope = accessTokenBo.getScope();
                if (StrUtil.isBlank(scope) || !scope.contains(HuaweiConstant.STEP_READ) || !scope.contains(HuaweiConstant.DISTANCE_READ) || !scope.contains(HuaweiConstant.CALORIES_READ)) {
                    //有一些需要授权的  用户没授权
                    logger.info("用户绑定手环 未完整授权 用户id: " + String.valueOf(userId));
                    user.setHuaweiAuthorizeComplete(1);
                } else {
                    //完整授权
                    user.setHuaweiAuthorizeComplete(2);
                }
                //同时更新用户手环绑定状态
                user.setBindFlag("1");
                //更新手环绑定时间
                user.setBindTime(LocalDateTime.now());
                userMapper.updateUser(user);

                //记录执行日志成功
                UserOperateLog userOperateLog = new UserOperateLog();
                userOperateLog.setUserId(userId);
                userOperateLog.setType(1);
                userOperateLog.setStatus("0");
                userOperateLog.setCreateTime(LocalDateTime.now());
                userOperateLogMapper.insertUserOperateLog(userOperateLog);
            } else {
                String string = response.body().string();
                logger.info("用户绑定手环 用户id: " + String.valueOf(userId) + " 获取accesstoken 失败: " + string);
                //记录执行日志
                UserOperateLog userOperateLog = new UserOperateLog();
                userOperateLog.setUserId(userId);
                userOperateLog.setType(1);
                userOperateLog.setStatus("1");
                String errorMsg = StringUtils.substring(string, 0, 2000);
                userOperateLog.setExceptionInfo(errorMsg);
                userOperateLog.setCreateTime(LocalDateTime.now());
                userOperateLogMapper.insertUserOperateLog(userOperateLog);

                throw new BusinessException("获取华为 accessToken 失败");
            }
        } catch (BusinessException e) {
            logger.info("用户绑定手环 用户id: " + String.valueOf(userId) + " 获取accesstoken 失败了 : ", e);
        } catch (Exception e) {
            logger.info("用户绑定手环 用户id: " + String.valueOf(userId) + " 获取accesstoken 失败 异常: ", e);
            //记录执行日志
            UserOperateLog userOperateLog = new UserOperateLog();
            userOperateLog.setUserId(userId);
            userOperateLog.setType(1);
            userOperateLog.setStatus("1");
            String errorMsg = StringUtils.substring(ExceptionUtils.getExceptionMessage(e), 0, 2000);
            userOperateLog.setExceptionInfo(errorMsg);
            userOperateLog.setCreateTime(LocalDateTime.now());
            userOperateLogMapper.insertUserOperateLog(userOperateLog);

            throw new BusinessException("获取华为 accessToken 失败");
        }
    }

    /**
     * 从缓存中获取accessToken
     * @param userId
     * @return
     */
    @Override
    public String getAccessTokenFromCache(int userId) {
        //从redis中获取accessToken
        AccessTokenBo accessTokenBo = JsonUtil.parseObject(JsonUtil.toJSONString(redisUtils.get(CacheConstant.HUAWEI_ACCESS_TOKEN_PREFIX + userId)), AccessTokenBo.class);
        if (accessTokenBo == null) {
            //redis中没有，那就应该重新授权了
            return null;
        }
        //检查access_token是否过期
        String expire = accessTokenBo.getAccessTokenExpire();
        LocalDateTime expireTime = LocalDateTimeUtil.parse(expire, "yyyy-MM-dd HH:mm:ss");
        if (expireTime.isBefore(LocalDateTime.now())) {
            //accessToken 失效了,需要使用refreshToken刷新accessToken
            HuaweiAccessTokenBo huaweiAccessTokenBo = refreshAcessToken(accessTokenBo.getRefreshToken());
            //更新redis中的accessToken
            accessTokenBo = accessTokenBo.buildByHuaweiAccessTokenBo(huaweiAccessTokenBo);
            redisUtils.set(CacheConstant.HUAWEI_ACCESS_TOKEN_PREFIX + userId, accessTokenBo); //将新的accessToken,过期时间,refreshToken放入缓存
        }
        return accessTokenBo.getAccessToken();

    }

    /**
     * 刷新华为 accessToken
     * @param refreshToken
     * @return
     */
    private HuaweiAccessTokenBo refreshAcessToken(String refreshToken) {
        OkHttpClient okHttpClient = new OkHttpClient();

        RequestBody body = new FormBody.Builder()
                .add("grant_type", "refresh_token")
                .add("refresh_token", refreshToken)
                .add("client_id", HuaweiConstant.CLIENT_ID)
                .add("client_secret", HuaweiConstant.CLIENT_SECRET)
                .add("access_type", "offline")
                .build();
        Request request = new Request.Builder()
                .url(HuaweiConstant.TOKEN_URL)
                .post(body)
                .build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            if (response.isSuccessful()) { // 判断是否成功
                String string = response.body().string();
                System.out.println(string);
                logger.info("huawei refreshAccessToken response: " + string);
                HuaweiAccessTokenBo huaweiAccessTokenBo = JsonUtil.parseObject(string, HuaweiAccessTokenBo.class);
                if (huaweiAccessTokenBo == null) {
                    throw new BusinessException("刷新华为 accessToken 失败");
                }
                //更新accessToken,refreshToken和accessToken的过期时间
                return huaweiAccessTokenBo;
            } else {
                logger.info("huawei refreshAccessToken http status: " + response.code() + "  " + response.message());
                throw new BusinessException("刷新华为 accessToken 失败");
            }
        } catch (Exception e) {
            logger.info("huawei refreshAccessToken exception: " + e.getMessage());
            throw new BusinessException("刷新华为 accessToken 失败");
        }
    }

    /**
     * 取消华为手环授权
     * @param userId
     * @return
     */
    @Override
    public boolean unAuthorize(int userId) {
        String accessToken = getAccessTokenFromCache(userId);
        if (accessToken == null) {
            throw new BusinessException("您已取消授权");
        }
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url(HuaweiConstant.UN_AUTHORIZE_URL)
                .delete()
                .addHeader("Content-typ","application/json; charset=UTF-8")
                .addHeader("Authorization", "Bearer " + accessToken)
                .addHeader("x-client-id", HuaweiConstant.CLIENT_ID)
                .addHeader("x-version","1.00")
                .build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            if (response.isSuccessful()) { // 判断是否成功
                String string = response.body().string();
                logger.info("用户解绑手环 用户id: " + String.valueOf(userId) + " 获取accesstoken: 成功" + string);
                //清理掉redis里面存的token
                redisUtils.del(CacheConstant.HUAWEI_ACCESS_TOKEN_PREFIX + userId);
                //同时更新用户手环绑定状态和授权状态
                User user = new User();
                user.setId(userId);
                user.setBindFlag("2");
                user.setHuaweiAuthorizeComplete(0);
                userMapper.updateUser(user);

                //记录执行日志成功
                UserOperateLog userOperateLog = new UserOperateLog();
                userOperateLog.setUserId(userId);
                userOperateLog.setType(2);
                userOperateLog.setStatus("0");
                userOperateLog.setCreateTime(LocalDateTime.now());
                userOperateLogMapper.insertUserOperateLog(userOperateLog);

                return true;
            } else {
                String string = response.body().string();
                logger.info("用户解绑手环 用户id: " + String.valueOf(userId) + " 取消华为授权失败: 失败" + string);
                //记录执行日志
                UserOperateLog userOperateLog = new UserOperateLog();
                userOperateLog.setUserId(userId);
                userOperateLog.setType(2);
                userOperateLog.setStatus("1");
                String errorMsg = StringUtils.substring(string, 0, 2000);
                userOperateLog.setExceptionInfo(errorMsg);
                userOperateLog.setCreateTime(LocalDateTime.now());
                userOperateLogMapper.insertUserOperateLog(userOperateLog);
            }
        } catch (Exception e) {
            logger.info("用户解绑手环 用户id: " + String.valueOf(userId) + " 取消华为授权失败: 失败", e);
            //记录执行日志
            UserOperateLog userOperateLog = new UserOperateLog();
            userOperateLog.setUserId(userId);
            userOperateLog.setType(2);
            userOperateLog.setStatus("1");
            String errorMsg = StringUtils.substring(ExceptionUtils.getExceptionMessage(e), 0, 2000);
            userOperateLog.setExceptionInfo(errorMsg);
            userOperateLog.setCreateTime(LocalDateTime.now());
            userOperateLogMapper.insertUserOperateLog(userOperateLog);
        }
        return false;
    }

    /**
     * 获取华为accessToken里面包含的信息
     * @param accessToken
     * @return
     */
    @Override
    public TokenInfoBo getAccessTokenInfo(String accessToken) {
        OkHttpClient okHttpClient = new OkHttpClient();

        RequestBody body = new FormBody.Builder()
                .add("open_id", "OPENID")
                .add("access_token", accessToken)
                .build();
        Request request = new Request.Builder()
                .url(HuaweiConstant.TOKEN_INFO_URL)
                .post(body)
                .addHeader("nsp_svc","huawei.oauth2.user.getTokenInfo")
                .addHeader("nsp_fmt", "JSON")
                .build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            if (response.isSuccessful()) { // 判断是否成功
                String string = response.body().string();
                logger.info("getTokenInfo response: " + string);
                TokenInfoBo tokenInfoBo = JsonUtil.parseObject(string, TokenInfoBo.class);
                if (tokenInfoBo == null) {
                    throw new BusinessException("获取华为tokenInfo 失败");
                }
                return tokenInfoBo;
            } else {
                logger.info("获取华为Token info: code : " + response.code() + " || message : " + response.message());
            }
        } catch (Exception e) {
            logger.info("获取华为Token info: " + e.getMessage());
            System.out.println(e.getMessage());
        }
        return null;
    }


    /**
     * 检查用户隐私授权状态
     * @param userId
     * @return int  1:隐私状态未授权， 2：隐私状态已授权， 3：取消了数据授权
     */
    @Override
    public int checkHealthKitPrivacy(int userId) {
        String accessToken = getAccessTokenFromCache(userId);
        if (accessToken == null) {
            throw new BusinessException("您已取消授权");
        }
        OkHttpClient okHttpClient = new OkHttpClient();
        //构建header
        Headers headers = buildHeaders(accessToken);
        Request request = new Request.Builder()
                .url(HuaweiConstant.PRIVACY_RRCORD_URL)
                .get()
                .headers(headers)
                .build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            if (response.isSuccessful()) { // 判断是否成功
                String string = response.body().string();
                logger.info("隐私授权状态查询 response: " + string);
                //将数据进行转换
                ObjectMapper objectMapper = new ObjectMapper();
                List<HealthKitPrivacyBo> healthKitPrivacyBos = objectMapper.readValue(string, new TypeReference<List<HealthKitPrivacyBo>>(){});
                if (healthKitPrivacyBos == null || healthKitPrivacyBos.isEmpty()) {
                    throw new BusinessException("隐私授权状态查询失败");
                }
                int opinion = healthKitPrivacyBos.get(0).getOpinion();
                return 1 == opinion ? 2 : 1;
            } else if (401 == response.code()) {
                return 3;
            }  else {
                logger.info("隐私授权状态查询: code : " + response.code() + " || message : " + response.message());
                throw new BusinessException("隐私授权状态查询失败");
            }
        } catch (Exception e) {
            logger.info("隐私授权状态查询: " + e.getMessage());
            throw new BusinessException("隐私授权状态查询失败");
        }
    }


    /** ***********************************************
     * 首先定时任务每天凌晨1点钟执行，获取每个用户前一天的统计数据（调用saveYesterdayHealthData 方法）
     *
     * 其次有个定时任务，每30分钟执行一次，获取每一个用户当天的统计数据
     *
     * 再有个定时任务，每30分钟执行一次，获取每一天用户当天的具体数据 (每个用户的具体数据只存当天)
     *
     * ************************************************
     */


    /********************************************** 获取某一天的详细数据，并存入redis start ****************************************************/

    /**
     * 获取用户某一天的华为健康数据详情，并存入redis中
     * @param userId
     */
    private boolean saveDaySampleSet(int userId, LocalDateTime localDateTime) {
        String accessToken = getAccessTokenFromCache(userId);
        if (StrUtil.isBlank(accessToken)) {
            return false;
        }
        OkHttpClient okHttpClient = new OkHttpClient();
        //构建请求内容
        QuerySampleSetRequest querySampleSetRequest = buildQuerySampleSetRequest(localDateTime);
        //构建请求body
        String jsonStr = JsonUtil.toJSONString(querySampleSetRequest);
        RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), jsonStr);
        //构建header
        Headers headers = buildHeaders(accessToken);
        //构建请求
        Request request = new Request.Builder()
                .url(HuaweiConstant.QUERY_SAMPLESET_URL)
                .post(body)
                .headers(headers)
                .build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            if (response.isSuccessful()) { // 判断是否成功
                String string = response.body().string();
                logger.info("getuserinfo response: " + string);
                //将数据进行转换
                HealthBo healthBo = JsonUtil.parseObject(string, HealthBo.class);
                //处理数据
                handleDetailHealthData(userId, localDateTime, healthBo);
            } else {
                logger.info("采样数据明细查询失败: code : " + response.code() + " || message : " + response.message());
            }
        } catch (Exception e) {
            logger.info("采样数据明细查询失败: " + e.getMessage());
        }
        return true;
    }

    /**
     * 构建华为采样数据明细查询 接口的请求参数
     * @param localDateTime
     * @return
     */
    private QuerySampleSetRequest buildQuerySampleSetRequest(LocalDateTime localDateTime) {
        QuerySampleSetRequest querySampleSetRequest = new QuerySampleSetRequest();
        long startTime = LocalDateTimeUtil.beginOfDay(localDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        long endTime = LocalDateTimeUtil.endOfDay(localDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        querySampleSetRequest.setStartTime(startTime);
        querySampleSetRequest.setEndTime(endTime);

        List<QuerySampleSetRequest.PolymerizeWithBean> polymerizeWithBeans = new ArrayList<>();
        QuerySampleSetRequest.PolymerizeWithBean polymerizeWithBean = new QuerySampleSetRequest.PolymerizeWithBean();
        polymerizeWithBean.setDataTypeName("com.huawei.continuous.steps.delta");
        polymerizeWithBeans.add(polymerizeWithBean);
        querySampleSetRequest.setPolymerizeWith(polymerizeWithBeans);
        return querySampleSetRequest;
    }

    /**
     * 构建请求header
     * @param accessToken
     * @return
     */
    private Headers buildHeaders(String accessToken) {
        Headers.Builder builder = new Headers.Builder();
        builder.add("Content-type", "application/json; charset=UTF-8");
        builder.add("Authorization", "Bearer " + accessToken);
        builder.add("x-client-id", HuaweiConstant.CLIENT_ID);
        builder.add("x-version","1.00");
        Headers headers = builder.build();
        return headers;
    }

    /**
     * 处理某一天的采样数据明细
     * @param userId
     * @param dateTime
     * @param healthBo
     */
    private void handleDetailHealthData(int userId, LocalDateTime dateTime, HealthBo healthBo) {
        ZoneId zone = ZoneId.systemDefault();
        for (HealthBo.GroupBean groupBean : healthBo.getGroup()) {
            List<Integer> dataList = new ArrayList<Integer>();
            List<String> timeList = new ArrayList<String>();

            String dataCollectorId = groupBean.getSampleSet().get(0).getDataCollectorId();

            if (groupBean.getSampleSet().get(0).getSamplePoints().isEmpty()) {
                //没有数据
                //redis里面存空
                saveDetailHealthData(userId, dateTime, dataList, timeList, dataCollectorId);
                continue;
            }
            List<HealthBo.GroupBean.SampleSetBean.SamplePointsBean> samplePointsBeans = groupBean.getSampleSet().get(0).getSamplePoints();
            for (HealthBo.GroupBean.SampleSetBean.SamplePointsBean samplePointsBean : samplePointsBeans) {
                long endTimeLong = samplePointsBean.getEndTime();
                Instant instant = Instant.ofEpochMilli(endTimeLong/1000000);
                LocalDateTime endTime = LocalDateTime.ofInstant(instant, zone);
                String time = LocalDateTimeUtil.format(endTime, "HH:mm");
                List<HealthBo.GroupBean.SampleSetBean.SamplePointsBean.ValueBean> valueBeans = samplePointsBean.getValue();
                for (HealthBo.GroupBean.SampleSetBean.SamplePointsBean.ValueBean valueBean : valueBeans) {
                    String fieldName = valueBean.getFieldName();
                    if ("steps_delta".equals(fieldName)) {
                        //步数
                        timeList.add(time);
                        dataList.add(valueBean.getIntegerValue());
                    }
                }
            }

            //存入redis
            saveDetailHealthData(userId, dateTime, dataList, timeList, dataCollectorId);
        }
    }

    /**
     * 将某一天的采样数据明细存入redis
     * @param userId
     * @param dateTime
     * @param dataList
     * @param timeList
     * @param dataCollectorId
     */
    private void saveDetailHealthData(int userId, LocalDateTime dateTime, List<Integer> dataList, List<String> timeList, String dataCollectorId) {
        String date = dateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        long expire = 1800L;   //过期时间
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("time",timeList);
        hashMap.put("data",dataList);

        if ("derived:com.huawei.continuous.steps.delta:com.huawei.hwid:merged".equals(dataCollectorId)) {
            //步数
            redisUtils.set(CacheConstant.HUAWEI_HEALTH_DETAIL_STEP_DATA_PREFIX + date + "_" + userId, hashMap, expire);
        }
    }

    /**
     * 获取某个用户某一天的具体采样数据
     * @param userId 1：步数，2：心率，3：血氧
     * @param type
     * @param dateTime
     * @param forceRefresh 是否强制从华为拉取数据   1：是，    0：否
     * @return
     */
    @Override
    public HashMap<String, Object> getDetailHealthData(int userId, int type, LocalDateTime dateTime, int forceRefresh) {
        User user = userMapper.selectUserById(userId);

        String date = dateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String redisKey = "";
        if (type == 1) {
            redisKey = CacheConstant.HUAWEI_HEALTH_DETAIL_STEP_DATA_PREFIX + date + "_" + userId;
        }
        if (1 == forceRefresh) {
            saveDaySampleSet(userId, dateTime);
            Object jsonStr = redisUtils.get(redisKey);
            if (jsonStr == null) {
                return null;
            }
            HashMap<String, Object> hashMap = (HashMap<String, Object>) jsonStr;
            return hashMap;
        } else {
            Object jsonStr = redisUtils.get(redisKey);
            if (jsonStr == null) {
                //重新去华为那里获取
                saveDaySampleSet(userId, dateTime);
                jsonStr = redisUtils.get(redisKey);
            }
            if (jsonStr == null) {
                return null;
            }
            HashMap<String, Object> hashMap = (HashMap<String, Object>) jsonStr;
            return hashMap;
        }
    }

    /********************************************** 获取某一天的详细数据，并存入redis  end ****************************************************/




    /********************************************** 获取某一天的统计数据，并存入redis  start ****************************************************/

    /**
     * 获取用户某一天的华为健康数据统计结果，并存入redis中
     * @param userId
     * @param localDateTime
     * @return
     */
    private boolean saveDailyPolymerize(int userId, LocalDateTime localDateTime) {
        String accessToken = getAccessTokenFromCache(userId);
        if (StrUtil.isBlank(accessToken)) {
            return false;
        }
        OkHttpClient okHttpClient = new OkHttpClient();
        //构建请求参数
        HashMap<String, Object> hashMap = buildDailyPolymerizeRequest(localDateTime);
        //构建请求body
        String jsonStr = JsonUtil.toJSONString(hashMap);
        RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), jsonStr);
        //构建header
        Headers headers = buildHeaders(accessToken);
        Request request = new Request.Builder()
                .url(HuaweiConstant.DAILY_POLYMERIZE_URL)
                .post(body)
                .headers(headers)
                .build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            if (response.isSuccessful()) { // 判断是否成功
                String string = response.body().string();
                logger.info("dailyPolymerize response: " + string);
                System.out.println(string);
                //将数据进行转换
                HealthBo healthBo = JsonUtil.parseObject(string, HealthBo.class);
                //处理数据
                handleDailyPolymerizeData(userId, localDateTime, healthBo);
            } else {
                logger.info("采样数据统计查询失败: code : " + response.code() + " || message : " + response.message());
            }
        } catch (Exception e) {
            logger.info("采样数据统计查询失败: " + e.getMessage());
            System.out.println(e.getMessage());
        }
        return true;
    }

    /**
     * 构建华为采样数据统计查询 接口的请求参数
     * @param localDateTime
     * @return
     */
    private HashMap<String, Object> buildDailyPolymerizeRequest(LocalDateTime localDateTime) {
        String startDay = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        //构建请求参数
        List<String> dataTypes = new ArrayList<String>();
        dataTypes.add("com.huawei.continuous.steps.delta");     //步数
        dataTypes.add("com.huawei.continuous.distance.delta");  //距离
        dataTypes.add("com.huawei.continuous.calories.burnt");  //卡路里
        HashMap<String, Object> hashMap = new HashMap<String, Object>();
        hashMap.put("startDay", startDay);
        hashMap.put("endDay", startDay);
        hashMap.put("timeZone","+0800");
        hashMap.put("dataTypes", dataTypes);
        return hashMap;
    }

    /**
     * 处理某一天的采样数据统计结果
     * @param userId
     * @param healthBo
     */
    private void handleDailyPolymerizeData(int userId, LocalDateTime localDateTime, HealthBo healthBo) {
        HashMap<String, Object> dataHashMap = new HashMap<String, Object>();
        String date = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));

        //2天前的采样统计数据 应该不会变
        //2天前的时间
        LocalDateTime twoDayBefore = LocalDateTimeUtil.offset(LocalDateTimeUtil.beginOfDay(LocalDateTime.now()),-2, ChronoUnit.DAYS);
        long expire = 1800L;   //过期时间
        if (localDateTime.isBefore(twoDayBefore)) {
            expire = 0L;
        }

        if (healthBo == null) {
            redisUtils.set(CacheConstant.HUAWEI_HEALTH_DAY_DATA_PREFIX + date + "_" + userId, dataHashMap, expire);
            return;
        }
        HealthBo.GroupBean groupBean = healthBo.getGroup().get(0);
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime startTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(groupBean.getStartTime()), zone);
        List<HealthBo.GroupBean.SampleSetBean> sampleSetBeans = groupBean.getSampleSet();

        dataHashMap.put("time",startTime.toLocalDate().toString());

        for (HealthBo.GroupBean.SampleSetBean sampleSetBean : sampleSetBeans) {
            List<HealthBo.GroupBean.SampleSetBean.SamplePointsBean> samplePointsBeans = sampleSetBean.getSamplePoints();
            for (HealthBo.GroupBean.SampleSetBean.SamplePointsBean samplePointsBean : samplePointsBeans) {
                List<HealthBo.GroupBean.SampleSetBean.SamplePointsBean.ValueBean> valueBeans = samplePointsBean.getValue();
                for (HealthBo.GroupBean.SampleSetBean.SamplePointsBean.ValueBean valueBean : valueBeans) {
                    String fieldName = valueBean.getFieldName();
                    if (valueBean.getFloatValue() != null) {
                        System.out.println(valueBean.getFloatValue());
                        dataHashMap.put(fieldName,valueBean.getFloatValue());
                    } else if (valueBean.getIntegerValue() != null) {
                        dataHashMap.put(fieldName,valueBean.getIntegerValue());
                    }
                }
            }
        }
        //将数据存入redis
        redisUtils.set(CacheConstant.HUAWEI_HEALTH_DAY_DATA_PREFIX + date + "_" + userId, dataHashMap, expire);
    }

    /**
     * 获取某个用户某一天的采样数据统计
     * @param userId
     * @param dateTime
     * @return
     */
    @Override
    public HashMap<String, Object> getStatHealthData(int userId, LocalDateTime dateTime, int forceRefresh) {
        String date = dateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String redisKey = CacheConstant.HUAWEI_HEALTH_DAY_DATA_PREFIX + date + "_" + userId;
        if (1 == forceRefresh) {
            saveDailyPolymerize(userId, dateTime);
            Object jsonStr = redisUtils.get(redisKey);
            if (jsonStr == null) {
                return null;
            }
            HashMap<String, Object> dataHashMap = (HashMap<String, Object>) jsonStr;
            return  dataHashMap;
        } else {
            Object jsonStr = redisUtils.get(redisKey);
            if (jsonStr == null) {
                //重新去华为那里获取
                saveDailyPolymerize(userId, dateTime);
                jsonStr = redisUtils.get(redisKey);
            }
            if (jsonStr == null) {
                return null;
            }
            HashMap<String, Object> dataHashMap = (HashMap<String, Object>) jsonStr;
            return  dataHashMap;
        }
    }

    /********************************************** 获取某一天的统计数据，并存入redis  end ****************************************************/





    /********************************************** 获取折线图要的数据格式  start ****************************************************/

    /**
     * 获取一段时间内的数据，并将数据结构转换为折线图展现格式
     * @param userId
     * @param type     数据类型   1：步数，2：心率，3：血氧
     * @param start
     * @param end
     * @param forceRefresh  是否强制从华为拉取数据   1：是，    0：否
     * @return
     */
    @Override
    public HashMap<String, Object> getDailyHealthDataList(int userId, int type, LocalDate start, LocalDate end, int forceRefresh) {
        List<LocalDate> dateList = LocalDateUtils.collectLocalDates(start, end);
        HashMap<String, Object> hashMap = new HashMap<>();
        List<Integer> dataList = new ArrayList<Integer>();
        List<String> timeList = new ArrayList<String>();
        BigDecimal distance = new BigDecimal("0");  //总距离
        BigDecimal calorie = new BigDecimal("0");   //总消耗卡路里
        Integer totalSteps = 0;  //总步数
        //获取这一段时间中每天的步数总和，平均心率，平均血氧
        for (LocalDate dateItem : dateList) {
            //获取统计数据
            HashMap<String, Object> statHashMap = getStatHealthData(userId, dateItem.atStartOfDay(), forceRefresh);
            if (statHashMap != null) {
                timeList.add(LocalDateTimeUtil.format(dateItem, "MM/dd"));
                if (type == 1) {
                    Integer step = statHashMap.containsKey("steps") ? (Integer) statHashMap.get("steps") : null;   //步数
                    dataList.add(step);
                    distance = CaculateUtils.BigDecimalAddDouble(distance, (statHashMap.containsKey("steps") ?(Double) statHashMap.get("distance") : 0)); //求总距离
                    calorie = CaculateUtils.BigDecimalAddDouble(calorie, (statHashMap.containsKey("calories_total") ?(Double) statHashMap.get("calories_total") : 0));  //求总消耗卡路里
                    totalSteps += step != null ? step : 0;
                }
            }
        }

        hashMap.put("time",timeList);
        if (1 == type) {
            hashMap.put("data",dataList);
            hashMap.put("distance",CaculateUtils.m2km(distance));
            hashMap.put("calorie",calorie);
            hashMap.put("totalSteps",totalSteps);
        }
        return hashMap;
    }

    @Override
    public HashMap<String, Object> getDailyHealthDataListFromBluetooth(int userId, int type, LocalDate start, LocalDate end, int forceRefresh) {
        List<LocalDate> dateList = LocalDateUtils.collectLocalDates(start, end);
        HashMap<String, Object> hashMap = new HashMap<>();
        List<Object> dataList = new ArrayList<Object>();
        List<String> timeList = new ArrayList<String>();
        BigDecimal zeroData = new BigDecimal("0");
        BigDecimal distance = new BigDecimal("0");  //总距离
        BigDecimal calorie = new BigDecimal("0");   //总消耗卡路里
        Integer steps = 0;   //总步数

        BigDecimal maxHeartrate = new BigDecimal("0");  //最大心率
        BigDecimal meanHeartrate = new BigDecimal("0"); //平均心率
        BigDecimal heartrateSum = new BigDecimal("0");
        Integer heartrateCount = 0;

        BigDecimal maxSpo2 = new BigDecimal("0");   //最大血氧
        BigDecimal minSpo2 = new BigDecimal("0");  //最小血氧
        BigDecimal newestSpo2 = new BigDecimal("0");    //最新血氧

        for (LocalDate dateItem : dateList) {
            LocalDateTime startOfDay = LocalDateTime.of(dateItem, LocalTime.of(0, 0, 0));
            LocalDateTime endOfDay = LocalDateTime.of(dateItem, LocalTime.of(23, 59, 59));
            List<HuaweiHealthBluetoohDetail> list = huaweiHealthBluetoohDetailMapper.selectDataByUserIdAndTime(userId, startOfDay, endOfDay);
            if (type == 1) {
                //步数
                Integer daySteps = 0;
                BigDecimal dayDistance = new BigDecimal("0");
                BigDecimal dayCalories = new BigDecimal("0");
                for (HuaweiHealthBluetoohDetail huaweiHealthBluetoohDetail: list) {
                    daySteps += huaweiHealthBluetoohDetail.getSteps();
                    dayDistance = dayDistance.add(huaweiHealthBluetoohDetail.getDistance());
                    dayCalories =dayCalories.add(huaweiHealthBluetoohDetail.getCalories());
                }
                if (daySteps > 0) {
                    timeList.add(LocalDateTimeUtil.format(dateItem, "MM/dd"));
                    dataList.add(daySteps);
                    distance = distance.add(dayDistance);
                    calorie = calorie.add(dayCalories);
                    steps += daySteps;
                }
            } else if (type == 2) {
                //心率
                BigDecimal dayMaxHeartrate = new BigDecimal("0");
                BigDecimal dayMeanHeartrate = new BigDecimal("0");
                BigDecimal dayTotalHeartrate = new BigDecimal("0");
                Integer dayTotalCount = 0;
                for (HuaweiHealthBluetoohDetail huaweiHealthBluetoohDetail: list) {
                    if (dayMaxHeartrate.compareTo(huaweiHealthBluetoohDetail.getHeartrate()) < 0) {
                        dayMaxHeartrate = huaweiHealthBluetoohDetail.getHeartrate();
                    }
                    if (huaweiHealthBluetoohDetail.getHeartrate().compareTo(zeroData) > 0) {
                        dayTotalCount += 1;
                        dayTotalHeartrate = dayTotalHeartrate.add(huaweiHealthBluetoohDetail.getHeartrate());
                    }
                }
                if (dayTotalCount > 0) {
                    timeList.add(LocalDateTimeUtil.format(dateItem, "MM/dd"));
                    dayMeanHeartrate = dayTotalHeartrate.divide((new BigDecimal(dayTotalCount)), 0, RoundingMode.HALF_UP);
                    dataList.add(dayMeanHeartrate);
                    if (maxHeartrate.compareTo(dayMaxHeartrate) < 0) {
                        maxHeartrate = dayMaxHeartrate;
                    }
                    heartrateSum = heartrateSum.add(dayMeanHeartrate);
                    heartrateCount += 1;
                }
            } else {
                //血氧
                BigDecimal dayMaxSpo2 = new BigDecimal("0");   //最大血氧
                BigDecimal dayMinSpo2 = new BigDecimal("0");  //最小血氧
                BigDecimal dayTotalSpo2 = new BigDecimal("0");
                BigDecimal dayMeanSpo2 = new BigDecimal("0");
                Integer dayTotalCount = 0;
                for (HuaweiHealthBluetoohDetail huaweiHealthBluetoohDetail: list) {
                    if (huaweiHealthBluetoohDetail.getSpo2().compareTo(zeroData) > 0) {
                        if (dayMaxSpo2.compareTo(huaweiHealthBluetoohDetail.getSpo2()) < 0) {
                            dayMaxSpo2 = huaweiHealthBluetoohDetail.getSpo2();
                        }
                        if (dayMinSpo2.compareTo(huaweiHealthBluetoohDetail.getSpo2()) > 0) {
                            dayMinSpo2 = huaweiHealthBluetoohDetail.getSpo2();
                        }
                        newestSpo2 = huaweiHealthBluetoohDetail.getSpo2();
                        dayTotalCount += 1;
                        dayTotalSpo2 = dayTotalSpo2.add(huaweiHealthBluetoohDetail.getSpo2());
                    }
                }
                if (dayTotalCount > 0) {
                    timeList.add(LocalDateTimeUtil.format(dateItem, "MM/dd"));
                    dayMeanSpo2 = dayTotalSpo2.divide((new BigDecimal(dayTotalCount)), 0, RoundingMode.HALF_UP);
                    dataList.add(dayMeanSpo2);
                    if (maxSpo2.compareTo(dayMaxSpo2) < 0) {
                        maxSpo2 = dayMaxSpo2;
                    }
                    if (minSpo2.compareTo(dayMinSpo2) > 0) {
                        minSpo2 = dayMinSpo2;
                    }
                }
            }

        }
        hashMap.put("time",timeList);
        if (1 == type) {
            hashMap.put("data",dataList);
            hashMap.put("distance",CaculateUtils.m2km(distance));
            hashMap.put("calorie",calorie);
            hashMap.put("totalSteps",steps);
        } else if (type == 2) {
            hashMap.put("data",dataList);
            hashMap.put("max",maxHeartrate);
            hashMap.put("mean",heartrateSum.divide((new BigDecimal(heartrateCount)), 2, RoundingMode.HALF_UP));
        } else {
            hashMap.put("data",dataList);
            hashMap.put("max",maxSpo2);
            hashMap.put("min",minSpo2);
            hashMap.put("newest",newestSpo2);
        }
        return hashMap;
    }

    /**
     * 获取一天内的数据，并将数据结构转换为折线图展现格式
     * @param userId
     * @param type          数据类型   1：步数，2：心率，3：血氧
     * @param localDateTime
     * @param forceRefresh 是否强制从华为拉取数据   1：是，    0：否
     * @return
     */
    @Override
    public HashMap<String, Object> getMinutelyHealthDataList(int userId, int type, LocalDateTime localDateTime, int forceRefresh) {
        //具体数据
        HashMap<String, Object> detailHashMap = getDetailHealthData(userId, type, localDateTime, forceRefresh);
        //获取统计数据
        HashMap<String, Object> statHashMap = getStatHealthData(userId, localDateTime, forceRefresh);
        if (detailHashMap == null) {
            detailHashMap = setDefaultTimeList(type, (new ArrayList<String>()), (new ArrayList<Integer>()) );
            //设置一些默认值
            detailHashMap = setDefaultData(type, detailHashMap);
            return detailHashMap;
        } else {
            List<String> timeList = (List<String>) detailHashMap.get("time");
            List<Integer> dataList = (List<Integer>) detailHashMap.get("data");
            detailHashMap = setDefaultTimeList(type, timeList, dataList);
        }
        //统计数据
        if (statHashMap != null) {
            if (type == 1) {
                detailHashMap.put("distance", statHashMap.containsKey("distance") ? CaculateUtils.m2km((Double) statHashMap.get("distance")) : 0);
                detailHashMap.put("calorie", statHashMap.containsKey("calories_total") ? statHashMap.get("calories_total"): 0);
                detailHashMap.put("totalSteps", statHashMap.containsKey("steps") ? statHashMap.get("steps"): 0);
            }
        } else {
            //设置一些默认值
            detailHashMap = setDefaultData(type, detailHashMap);
        }
        return detailHashMap;
    }

    /**
     * 从蓝牙数据中获取一天内的数据，并将数据结构转换为折线图展现格式
     * @param userId
     * @param type
     * @param localDateTime
     * @param forceRefresh
     * @return
     */
    @Override
    public HashMap<String, Object> getMinutelyHealthDataListFromBluetooth(int userId, int type, LocalDateTime localDateTime, int forceRefresh) {
        LocalDateTime startOfDay = LocalDateTime.of(localDateTime.toLocalDate(), LocalTime.of(0, 0, 0));
        LocalDateTime endOfDay = LocalDateTime.of(localDateTime.toLocalDate(), LocalTime.of(23, 59, 59));
        List<HuaweiHealthBluetoohDetail> list = huaweiHealthBluetoohDetailMapper.selectDataByUserIdAndTime(userId, startOfDay, endOfDay);
        HashMap<String, Object> detailHashMap = new HashMap<String, Object>();
        if (list == null) {
            detailHashMap = setDefaultTimeList(type, (new ArrayList<String>()), (new ArrayList<Integer>()) );
            //设置一些默认值
            detailHashMap = setDefaultData(type, detailHashMap);
            return detailHashMap;
        }
        List<String> timeList = new ArrayList<String>();
        if (type == 1) {
            //步数
            BigDecimal distance = new BigDecimal("0");
            BigDecimal calorie = new BigDecimal("0");
            Integer totalSteps = 0;
            List<Integer> dataList = new ArrayList<Integer>();
            for (HuaweiHealthBluetoohDetail huaweiHealthBluetoohDetail: list) {
                String time = LocalDateTimeUtil.format(huaweiHealthBluetoohDetail.getEndTime(), "HH:mm");
                timeList.add(time);
                dataList.add(huaweiHealthBluetoohDetail.getSteps());
                distance = distance.add(huaweiHealthBluetoohDetail.getDistance());
                calorie = calorie.add(huaweiHealthBluetoohDetail.getCalories());
                totalSteps += huaweiHealthBluetoohDetail.getSteps();
            }
            detailHashMap.put("distance", distance.divide((new BigDecimal("1000")), 2, RoundingMode.HALF_UP));
            detailHashMap.put("calorie", calorie);
            detailHashMap.put("totalSteps", totalSteps);
            detailHashMap.put("data", dataList);
            detailHashMap.put("time", timeList);
        } else if (type == 2) {
            //心率
            BigDecimal maxHeartrate = new BigDecimal("0");
            BigDecimal meanHeartrate = new BigDecimal("0");
            BigDecimal totalHeartrate = new BigDecimal("0");
            Integer totalCount = 0;
            BigDecimal zeroData = new BigDecimal("0");
            List<BigDecimal> dataList = new ArrayList<BigDecimal>();
            for (HuaweiHealthBluetoohDetail huaweiHealthBluetoohDetail: list) {
                String time = LocalDateTimeUtil.format(huaweiHealthBluetoohDetail.getEndTime(), "HH:mm");
                timeList.add(time);
                dataList.add(huaweiHealthBluetoohDetail.getHeartrate());
                if (maxHeartrate.compareTo(huaweiHealthBluetoohDetail.getHeartrate()) < 0) {
                    maxHeartrate = huaweiHealthBluetoohDetail.getHeartrate();
                }
                if (huaweiHealthBluetoohDetail.getHeartrate().compareTo(zeroData) > 0) {
                    totalCount += 1;
                    totalHeartrate = totalHeartrate.add(huaweiHealthBluetoohDetail.getHeartrate());
                }
            }
            if (totalCount > 0) {
                meanHeartrate = totalHeartrate.divide((new BigDecimal(totalCount)), 0, RoundingMode.HALF_UP);
            }
            detailHashMap.put("mean", meanHeartrate);
            detailHashMap.put("max", maxHeartrate);
            detailHashMap.put("data", dataList);
            detailHashMap.put("time", timeList);
        } else {
            //血氧
            BigDecimal maxSpo2 = new BigDecimal("0");
            BigDecimal minSpo2 = new BigDecimal("0");
            BigDecimal newestSpo2 = new BigDecimal("0");
            BigDecimal zeroData = new BigDecimal("0");
            List<BigDecimal> dataList = new ArrayList<BigDecimal>();
            for (HuaweiHealthBluetoohDetail huaweiHealthBluetoohDetail: list) {
                String time = LocalDateTimeUtil.format(huaweiHealthBluetoohDetail.getEndTime(), "HH:mm");
                timeList.add(time);
                dataList.add(huaweiHealthBluetoohDetail.getSpo2());
                if (huaweiHealthBluetoohDetail.getSpo2().compareTo(zeroData) > 0) {
                    if (maxSpo2.compareTo(huaweiHealthBluetoohDetail.getSpo2()) < 0) {
                        maxSpo2 = huaweiHealthBluetoohDetail.getSpo2();
                    }
                    if (minSpo2.compareTo(huaweiHealthBluetoohDetail.getSpo2()) > 0) {
                        minSpo2 = huaweiHealthBluetoohDetail.getSpo2();
                    }
                    newestSpo2 = huaweiHealthBluetoohDetail.getSpo2();
                }
            }
            detailHashMap.put("min", minSpo2);
            detailHashMap.put("max", maxSpo2);
            detailHashMap.put("newest", newestSpo2);
            detailHashMap.put("data", dataList);
            detailHashMap.put("time", timeList);
        }
        return detailHashMap;
    }

    /**
     * 设置一些默认值
     * @param type
     * @param hashMap
     * @return
     */
    private HashMap<String, Object> setDefaultData(int type, HashMap<String, Object> hashMap) {
        if (type == 1) {
            hashMap.put("distance", 0);
            hashMap.put("calorie", 0);
            hashMap.put("totalSteps", 0);
        } else if (type == 2) {
            hashMap.put("mean", 0);
            hashMap.put("max", 0);
        } else {
            hashMap.put("min", 0);
            hashMap.put("max", 0);
            hashMap.put("newest", 0);
        }
        return hashMap;
    }

    /**
     *  设置一些默认值
     * @param type  数据类型   1：步数，2：心率，3：血氧
     * @param timeList
     * @param dataList
     * @return
     */
    private HashMap<String, Object> setDefaultTimeList(int type, List<String> timeList, List<Integer> dataList) {
        Map<String,Integer> dataMap = timeList.stream().collect(Collectors.toMap(key->key,key->dataList.get(timeList.indexOf(key))));
        List<String> allTimeList = new ArrayList<String>();
        List<Integer> allDataList = new ArrayList<>();
        for (int h=0; h<24; h++) {
            Integer topHalf = 0; //00-30部分的和（包前不包后）
            Integer topHalfCount = 0;
            Integer secondHalf = 0; //30-59部分的和（包前不包后）
            Integer secondHalfCount = 0;
            if (type != 2) {
                String topHalfTimeStr = h<10 ? ("0"+h+":00 - 0"+h+":30") : (String.valueOf(h) +":00 - " +String.valueOf(h) + ":30");
                String secondHalfTimeStr = h<10 ? ("0"+h+":30 - 0"+(h+1)+":00") : (String.valueOf(h) +":30 - " +String.valueOf(h+1) + ":00");
                allTimeList.add(topHalfTimeStr);
                allTimeList.add(secondHalfTimeStr);
            }
            for (int m=0; m<60; m++) {
                String time = (h<10 ? "0"+h : String.valueOf(h)) + ":" + (m<10 ? "0"+m : String.valueOf(m));
                if (type != 2) {
                    if (m<30) {
                        //00 - 30
                        if (dataMap.containsKey(time)) {
                            topHalf += dataMap.get(time);
                            topHalfCount += 1;
                        }
                    } else {
                        //30 - 59
                        if (dataMap.containsKey(time)) {
                            secondHalf += dataMap.get(time);
                            secondHalfCount += 1;
                        }
                    }
                } else {
                    allTimeList.add(time);
                    if (dataMap.containsKey(time)) {
                        allDataList.add(dataMap.get(time));
                    } else {
                        allDataList.add(null);
                    }
                }
            }
            if (type == 1) {
                //步数求和
                allDataList.add(topHalfCount > 0 ? topHalf : null);
                allDataList.add(secondHalfCount > 0 ? secondHalf : null);
            }
        }
        HashMap<String, Object> res = new HashMap<>();
        res.put("time",allTimeList);
        res.put("data",allDataList);
        return res;
    }

    /********************************************** 获取折线图要的数据格式  end ****************************************************/

    public void handleBluetoothData(int userId, List<ReceviceDataRequest> list) {
        User user = userMapper.selectUserById(userId);
        long baseTime = 0;
        String startTime = null;
        long timeOffset = 0;
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getTag().equals("03")) {
                baseTime = Long.parseLong(list.get(i).getVal(), 16);
            }

            if (list.get(i).getTag().equals("05")) {
                DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                if (timeOffset != 0) {
                    startTime = dateFormat.format(new Date((baseTime + timeOffset) * 1000));
                } else {
                    startTime = dateFormat.format(new Date(baseTime * 1000));
                }
                timeOffset = (Long.parseLong(list.get(i).getVal(), 16) + 1L) * 60;
            }
            if (list.get(i).getTag().equals("06")) {
                DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String endTime = dateFormat.format(new Date((baseTime + timeOffset) * 1000));
                HuaweiHealthBluetoohDetail huaweiHealthBluetoohDetail = getSportData(userId, startTime, endTime, list.get(i).getVal());
                if (!"1".equals(user.getBindFlag())) {
                    user.setBindFlag("1");
                    user.setFirstBindTime(LocalDateTime.now());
                    userMapper.updateUser(user);
                }
                //检查这个时间的数据有没有插入成功
                Integer id = huaweiHealthBluetoohDetailMapper.selectIdByUserIdAndTime(userId, huaweiHealthBluetoohDetail.getStartTime(), huaweiHealthBluetoohDetail.getEndTime());
                if (id == null) {
                    huaweiHealthBluetoohDetailMapper.insertHuaweiHealthBluetoohDetail(huaweiHealthBluetoohDetail);
                } else {
                    huaweiHealthBluetoohDetail.setUpdateTime(LocalDateTime.now());
                    huaweiHealthBluetoohDetail.setId(id);
                    huaweiHealthBluetoohDetailMapper.updateHuaweiHealthBluetoohDetail(huaweiHealthBluetoohDetail);
                }
            }
        }
    }

    public void receiveData(int userId, List<ReceviceDataRequest> list) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("userId", userId);
        map.put("list", list);
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            String jsonString = objectMapper.writeValueAsString(map);
            redisUtils.lPush("huawei_bluetooth_data_list", jsonString);
        } catch (Exception e) {
            throw new BusinessException("接收数据失败");
        }
    }

    public HuaweiHealthBluetoohDetail getSportData(int userId, String startTimeString, String endTimeString, String val) {
        String type1 = String.format("%8s", Integer.toBinaryString(Integer.parseInt(val.substring(0, 2), 16))).replace(' ', '0');

        char sampleBitma = type1.charAt(7);
        int sampleBitmaVal = 0;
        char step = type1.charAt(6);
        int stepVal = 0;
        char calorie = type1.charAt(5);
        int calorieVal = 0;
        char distance = type1.charAt(4);
        int distanceVal = 0;
        char height = type1.charAt(3);
        int heightVal = 0;
        char heartRateValOld = type1.charAt(2);
        int heartRateValOldAvg = 0;
        char heartRate = type1.charAt(1);
        int heartRateVal = 0;
        int bloodOxygenVal = 0;
        int heartRateValNew = 0;
        int heartRateValNewAvg = 0;
        char bit7 = type1.charAt(0);

        if (bit7 == '1') {
            String type2 = String.format("%8s", Integer.toBinaryString(Integer.parseInt(val.substring(2, 4), 16))).replace(' ', '0');
            char bloodOxygen = type2.charAt(7);
            int offset = 4;

            if (sampleBitma == '1') {
                sampleBitmaVal = Integer.parseInt(val.substring(offset, offset + 2), 16);
                offset += 2;
            }
            if (step == '1') {
                stepVal = Integer.parseInt(val.substring(offset, offset + 4), 16);
                offset += 4;
            }
            if (calorie == '1') {
                calorieVal = Integer.parseInt(val.substring(offset, offset + 4), 16);
                offset += 4;
            }
            if (distance == '1') {
                distanceVal = Integer.parseInt(val.substring(offset, offset + 4), 16);
                offset += 4;
            }
            if (height == '1') {
                heightVal = Integer.parseInt(val.substring(offset, offset + 4), 16);
                offset += 4;
            }
            if (heartRateValOld == '1') {
                heartRateValOldAvg = Integer.parseInt(val.substring(offset, offset + 2), 16);
                offset += 2;
            }
            if (heartRate == '1') {
                heartRateVal = Integer.parseInt(val.substring(offset, offset + 2), 16);
                offset += 2;
            }
            if (bloodOxygen == '1') {
                bloodOxygenVal = Integer.parseInt(val.substring(offset, offset + 2), 16);
                offset += 2;
            }
            if (heartRateValNew == '1') {
                heartRateValNewAvg = Integer.parseInt(val.substring(offset, offset + 2), 16);
                offset += 2;
            }
        } else {
            int offset = 2;
            if (sampleBitma == '1') {
                sampleBitmaVal = Integer.parseInt(val.substring(offset, offset + 2), 16);
                offset += 2;
            }
            if (step == '1') {
                stepVal = Integer.parseInt(val.substring(offset, offset + 4), 16);
                offset += 4;
            }
            if (calorie == '1') {
                calorieVal = Integer.parseInt(val.substring(offset, offset + 4), 16);
                offset += 4;
            }
            if (distance == '1') {
                distanceVal = Integer.parseInt(val.substring(offset, offset + 4), 16);
                offset += 4;
            }
            if (height == '1') {
                heightVal = Integer.parseInt(val.substring(offset, offset + 4), 16);
                offset += 4;
            }
            if (heartRateValOld == '1') {
                heartRateValOldAvg = Integer.parseInt(val.substring(offset, offset + 2), 16);
                offset += 2;
            }
            if (heartRate == '1') {
                heartRateVal = Integer.parseInt(val.substring(offset, offset + 2), 16);
                offset += 2;
            }
        }
        HuaweiHealthBluetoohDetail huaweiHealthBluetoohDetail = new HuaweiHealthBluetoohDetail();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime startTime = LocalDateTime.parse(startTimeString, formatter);
        LocalDateTime endTime = LocalDateTime.parse(endTimeString, formatter);
        huaweiHealthBluetoohDetail.setUserId(userId);
        huaweiHealthBluetoohDetail.setStartTime(startTime);
        huaweiHealthBluetoohDetail.setEndTime(endTime);
        huaweiHealthBluetoohDetail.setCreateTime(LocalDateTime.now());
        huaweiHealthBluetoohDetail.setSteps(stepVal);
        huaweiHealthBluetoohDetail.setCalories(BigDecimal.valueOf(calorieVal).divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP));
        huaweiHealthBluetoohDetail.setDistance(BigDecimal.valueOf(distanceVal));
        huaweiHealthBluetoohDetail.setHeight(BigDecimal.valueOf(heightVal));
        huaweiHealthBluetoohDetail.setHeartrate(BigDecimal.valueOf(heartRateVal));
        huaweiHealthBluetoohDetail.setRestHeartrate(BigDecimal.valueOf(heartRateValNewAvg));
        huaweiHealthBluetoohDetail.setRestOldHeartrate(BigDecimal.valueOf(heartRateValOldAvg));
        huaweiHealthBluetoohDetail.setSpo2(BigDecimal.valueOf(bloodOxygenVal));
        return huaweiHealthBluetoohDetail;
    }



}


